/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package syntacticanalyzer;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
import lexicalanalyzer.Symbol;
import lexicalanalyzer.Token;

/**
 *
 * @author Thiago
 */
public class AnalisadorSintatico {

    //Paths of reference files.
    private String pathTable;
    private String pathGrammar;
    //Symbol table.
    private HashMap<String, Symbol> symbolTable;
    //Token stack.
    private Stack<Token> tokenStack;
    //Syntactic table.
    private String[][] syntacticTable;
    private HashMap<String, Integer> colTokens;
    private HashMap<String, Integer> linStates;
    //Grammar
    private Grammar grammar;
    //Parser Stack
    private Stack<SinState> parserStack;

    public AnalisadorSintatico() {
        this.pathTable = "";
        this.pathGrammar = "";
        this.symbolTable = null;
        this.tokenStack = new Stack<>();
        this.colTokens = new HashMap<>();
        this.linStates = new HashMap<>();
        this.parserStack = new Stack<>();
        this.grammar = new Grammar();
    }

    public AnalisadorSintatico(ArrayList<Token> tokenList, HashMap<String, Symbol> symbolTable, String pathT, String pathG) {
        this.symbolTable = symbolTable;
        this.pathTable = pathT;
        this.pathGrammar = pathG;
        this.colTokens = new HashMap<>();
        this.linStates = new HashMap<>();
        this.grammar = new Grammar();

        this.tokenStack = new Stack<>();
        tokenStack.push(new Token("$", "$", 0));
        for (int i = tokenList.size() - 1; i >= 0; i--) {
            tokenStack.push(tokenList.get(i));
        }

        this.parserStack = new Stack<>();
        this.parserStack.push(new SinState("0", ""));

        this.initializeAnalyzer();
    }

    private void initializeAnalyzer() {
        //Check for missing reference files.
        if (getPathTable().isEmpty() || getPathGrammar().isEmpty()) {
            System.out.println("Reference files missing, analyzer could not perform initialization.");
            return;
        }

        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(new File(getPathTable())));
        } catch (FileNotFoundException ex) {
            System.out.println("Error in Syntactical Table reader: " + ex.getLocalizedMessage());
        }
        try {
            String dimensions = br.readLine().trim();
            String[] d = dimensions.split("~");
            syntacticTable = new String[Integer.valueOf(d[0])][Integer.valueOf(d[1])];

            for (int i = 0; i < Integer.valueOf(d[0]); i++) {
                linStates.put(String.valueOf(i), i);
            }
            String tokens = br.readLine().trim();
            String[] t = tokens.split(" ");
            for (int i = 0; i < t.length; i++) {
                colTokens.put(t[i], i);
            }

            int lin = 0;
            while (br.ready()) {
                String line = br.readLine();
                String[] aux = line.split(" ");
                System.arraycopy(aux, 0, syntacticTable[lin], 0, aux.length);
                ++lin;
            }

            br.close();
        } catch (IOException ex) {
            System.out.println("Error in readStates reading table file: " + ex.getLocalizedMessage());
        }

        readGrammar();
        parser();
    }

    public void readGrammar() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(new File(getPathGrammar())));
        } catch (FileNotFoundException ex) {
            System.out.println("Error in Grammar reader: " + ex.getLocalizedMessage());
        }
        try {
            int count = 1;
            while (br.ready()) {
                Production p = new Production();
                String line = br.readLine();
                String[] prod = line.split(":=");
                p.setLeft(prod[0].trim());
                String[] aux = prod[1].trim().split(" ");
                for (int i = 0; i < aux.length; i++) {
                    p.add(aux[i]);
                }
                grammar.put(String.valueOf(count), p);
                ++count;
            }
        } catch (IOException ex) {
            System.out.println("Error in readStates reading grammar file: " + ex.getLocalizedMessage());
        }
    }

    public void parser() {//Shift = empilhamento Reduce = redução xD
        while (!tokenStack.empty()) {
            ////////////////////////ARRUMAR OPERADORES
            String token;
            if (tokenStack.peek().getToken().equals("<operator>")) {
                token = "<";
            } else {
                token = tokenStack.peek().getToken();
            }
            String state = parserStack.peek().getState();
            if (state.equals("ERROR")) {
                System.out.println("ERROR - PARSER");
                System.out.println("Line: "+tokenStack.peek().getLine());
                break;
            }
            String action = syntacticTable[linStates.get(state)][colTokens.get(token)];
            String[] aux = action.split("-");
            String func = aux[0];
            String index = "";
            if (aux.length > 1) {
                index = aux[1];
            }

            try {
                Method m = this.getClass().getDeclaredMethod(func, index.getClass(), token.getClass());
                m.invoke(this, index, token);
            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            }
        }
    }

    public void SHIFT(String state, String token) {
        tokenStack.pop();
        parserStack.push(new SinState(state, token));
    }

    public void REDUCE(String state, String token) {
        int size = grammar.get(state).getRight().size();
        for (int i = 0; i < size; i++) {
            parserStack.pop();
//                if (parserStack.peek().getToken().equals(grammar.get(state).getRight().get(i))) {
//                    System.out.println(parserStack.peek().getToken()+"  "+parserStack.peek().getState());
//                    parserStack.pop();
//                }
        }       
        
        String newState = parserStack.peek().getState();
        String left = grammar.get(state).getLeft();
        SinState element = new SinState(syntacticTable[linStates.get(newState)][colTokens.get(left)], left);
        parserStack.push(element);
    }

    public void ACC(String state, String token) {
        System.out.println("ACCEPTED!");
        tokenStack.clear();
    }

    public void ERROR(String state, String token) {
        System.out.println("ERROR");
        System.out.println("Line: "+(tokenStack.peek().getLine()-1));
        tokenStack.clear();
    }

    public String getPathTable() {
        return pathTable;
    }

    public void setPathTable(String pathTable) {
        this.pathTable = pathTable;
    }

    public String getPathGrammar() {
        return pathGrammar;
    }

    public void setPathGrammar(String pathGrammar) {
        this.pathGrammar = pathGrammar;
    }

    public HashMap<String, Symbol> getSymbolTable() {
        return symbolTable;
    }

    public void setSymbolTable(HashMap<String, Symbol> symbolTable) {
        this.symbolTable = symbolTable;
    }

    public Stack<Token> getTokenStack() {
        return tokenStack;
    }

    public void setTokenStack(Stack<Token> tokenList) {
        this.tokenStack = tokenList;
    }

    public String[][] getSyntacticTable() {
        return syntacticTable;
    }

    public void setSyntacticTable(String[][] syntacticTable) {
        this.syntacticTable = syntacticTable;
    }
}
